package com.example.flowabledemo.demos.flow.component;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.runtime.ChangeActivityStateBuilder;
import org.flowable.task.api.Task;
import org.springframework.stereotype.Component;

import java.util.Objects;

/**
 * @Description: 撤回流程组件
 * @Author: LuanKay
 * @Date: 2025/2/14
 */
@Slf4j
@Component
@AllArgsConstructor
public class SkipNodeComponent {
    private final RuntimeService runtimeService;
    private final RepositoryService repositoryService;
    private final TaskService taskService;


    /**
     * 跳转到指定节点
     *
     * @param processId     流程实例 ID
     * @param targetTaskKey 目标节点的定义 Key
     * @throws IllegalArgumentException 如果流程或节点信息无效
     */
    public void skipNode(String processId, String targetTaskKey) {
        // 1. 获取当前任务节点
        Task currentTask = getCurrentTask(processId);

        // 2. 获取流程定义模型
        BpmnModel bpmnModel = getBpmnModel(currentTask.getProcessDefinitionId());

        // 3. 检查目标节点是否是用户任务
        validateTargetNode(bpmnModel, targetTaskKey);

        // 4. 执行跳转
        executeNodeSkip(processId, currentTask.getTaskDefinitionKey(), targetTaskKey);
    }

    /**
     * 获取当前任务节点
     *
     * @param processId 流程实例 ID
     * @return 当前任务对象
     * @throws IllegalArgumentException 如果当前任务不存在
     */
    private Task getCurrentTask(String processId) {
        Task currentTask = taskService.createTaskQuery()
                .processInstanceId(processId)
                .singleResult();

        if (Objects.isNull(currentTask)) {
            throw new IllegalArgumentException("当前任务不存在，无法跳转！");
        }

        log.info("当前任务节点：{}", currentTask.getTaskDefinitionKey());
        return currentTask;
    }

    /**
     * 获取流程定义模型
     *
     * @param processDefinitionId 流程定义 ID
     * @return 流程模型
     * @throws IllegalArgumentException 如果流程定义不存在
     */
    private BpmnModel getBpmnModel(String processDefinitionId) {
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);

        if (Objects.isNull(bpmnModel)) {
            throw new IllegalArgumentException("流程定义模型不存在！");
        }

        log.info("成功获取流程模型");
        return bpmnModel;
    }

    /**
     * 验证目标节点是否是用户任务
     *
     * @param bpmnModel     流程模型
     * @param targetTaskKey 目标节点 Key
     * @throws IllegalArgumentException 如果目标节点不是用户节点
     */
    private void validateTargetNode(BpmnModel bpmnModel, String targetTaskKey) {
        FlowElement targetElement = bpmnModel.getFlowElement(targetTaskKey);

        if (Objects.isNull(targetElement)) {
            throw new IllegalArgumentException("目标节点不存在！");
        }

        if (!(targetElement instanceof UserTask)) {
            throw new IllegalArgumentException("目标节点不是用户任务！");
        }

        log.info("目标节点验证成功：{}", targetTaskKey);
    }

    /**
     * 执行状态迁移（跳转到目标节点）
     *
     * @param processId      流程实例 ID
     * @param currentTaskKey 当前任务节点 Key
     * @param targetTaskKey  目标节点 Key
     */
    private void executeNodeSkip(String processId, String currentTaskKey, String targetTaskKey) {
        try {
            ChangeActivityStateBuilder changeActivityStateBuilder = runtimeService.createChangeActivityStateBuilder()
                    .processInstanceId(processId)
                    .moveActivityIdTo(currentTaskKey, targetTaskKey);

            changeActivityStateBuilder.changeState();

            log.info("成功跳转节点：{} -> {}", currentTaskKey, targetTaskKey);
        } catch (Exception e) {
            log.error("节点跳转失败：{}", e.getMessage());
            throw new RuntimeException("节点跳转失败，请检查流程状态！", e);
        }
    }


}
